package com.gzzyy.facerecognition

// Your IDE likely can auto-import these classes, but there are several
// different implementations so we list them here to disambiguate.
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.util.Size
import android.view.View
import android.widget.*
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.*
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.alibaba.fastjson.JSON
import com.bumptech.glide.Glide
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

const val Board = "rk30sdk"

class MainActivity : AppCompatActivity() {

    private val imageUploading = "图片上传中..."
    private val imageUploadFailed = "图片上传失败，点击重新拍照"
    private val imageAnalyzing = "智能分析中..."
    private val imageAnalyzeFailed = "智能分析失败，点击重新拍照"

    private lateinit var mainViewModel: MainViewModel

    private var preview: Preview? = null
    private var imageCapture: ImageCapture? = null
    private var camera: Camera? = null

    private lateinit var outputDirectory: File
    private lateinit var cameraExecutor: ExecutorService
    private var filePath: String = ""
    private lateinit var captureLayout: LinearLayout
    private lateinit var viewFinder: PreviewView
    private lateinit var imageView: ImageView
    private lateinit var statusTv: TextView
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (Build.BOARD == Board) {
            setContentView(R.layout.activity_main_rk)
        } else {
            setContentView(R.layout.activity_main)
        }
        captureLayout = findViewById(R.id.captureLayout)
        val cameraCaptureButton = findViewById<ImageButton>(R.id.camera_capture_button)
        cameraCaptureButton.setOnClickListener { takePhoto() }
        viewFinder = findViewById(R.id.viewFinder)
        imageView = findViewById(R.id.imageView)
        statusTv = findViewById(R.id.statusTv)
        statusTv.setOnClickListener {
            val text = statusTv.text.toString()
            if (text == imageUploadFailed || text == imageAnalyzeFailed) {
                captureLayout.visibility = View.GONE
            }
        }
        mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)
        mainViewModel.getUploadImageResponse().observe(this, Observer {
            if (it.isOk()) {
                val urlBean = it.data
                urlBean?.let {
                    Log.e("test", "upload image success url = ${urlBean.url}")
                    mainViewModel.uploadUrl(urlBean.url)
                    statusTv.text = imageAnalyzing
                }
            } else {
                statusTv.text = imageUploadFailed
                Log.e("test", "upload image error :${it.error_msg}")
            }
        })


        mainViewModel.getUploadUrlResponse().observe(this, Observer {
            if (it.isOk()) {
                val humanDataBean = it.data
                humanDataBean?.let {
                    Log.e("test", "upload url success $humanDataBean")
                    val humanInfoBean = humanDataBean.humanInfo
                    val personInfoList = humanInfoBean.person_info
                    if (!personInfoList.isNullOrEmpty()) {
                        val jsonString = JSON.toJSONString(personInfoList)
                        val intent = Intent(baseContext, ResultActivity::class.java)
                        intent.putExtra(ResultActivity.FilePath, filePath)
                        intent.putExtra(ResultActivity.Result, jsonString)
                        startActivity(intent)
                        captureLayout.visibility = View.GONE
                    } else {
                        Toast.makeText(baseContext, "未识别到人体", Toast.LENGTH_SHORT).show()
                        statusTv.text = imageAnalyzeFailed
                    }
                }
            } else {
                statusTv.text = imageAnalyzeFailed
                Toast.makeText(baseContext, it.error_msg, Toast.LENGTH_SHORT).show()
                Log.e("test", "upload url error :${it.error_msg}")
            }
        })

        // Request camera permissions
        if (allPermissionsGranted()) {
            startCamera()
        } else {
            ActivityCompat.requestPermissions(
                this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS
            )
        }
        outputDirectory = getOutputDirectory()
        cameraExecutor = Executors.newSingleThreadExecutor()
    }

    private fun startCamera() {

        val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

        cameraProviderFuture.addListener(Runnable {
            // Used to bind the lifecycle of cameras to the lifecycle owner
            val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()

            // Preview
            preview = Preview.Builder()
                .build()
            imageCapture = ImageCapture.Builder().setTargetResolution(Size(1440, 1920)).build()

            // Select back camera
            val cameraSelector =
                CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()

            try {
                // Unbind use cases before rebinding
                cameraProvider.unbindAll()

                // Bind use cases to camera
                camera = cameraProvider.bindToLifecycle(
                    this, cameraSelector, preview, imageCapture
                )
                preview?.setSurfaceProvider(viewFinder.createSurfaceProvider())
            } catch (exc: Exception) {
                Log.e(TAG, "Use case binding failed", exc)
            }

        }, ContextCompat.getMainExecutor(this))
    }


    private fun takePhoto() {
        // Get a stable reference of the modifiable image capture use case
        val imageCapture = imageCapture ?: return

        // Create timestamped output file to hold the image
        val photoFile = File(
            outputDirectory,
            SimpleDateFormat(
                FILENAME_FORMAT,
                Locale.CHINA
            ).format(System.currentTimeMillis()) + ".jpg"
        )

        // Create output options object which contains file + metadata
        val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()

        // Setup image capture listener which is triggered after photo has
        // been taken
        imageCapture.takePicture(
            outputOptions,
            ContextCompat.getMainExecutor(this),
            object : ImageCapture.OnImageSavedCallback {
                override fun onError(exc: ImageCaptureException) {
                    Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
                    viewFinder.post {
                        Toast.makeText(baseContext, exc.message, Toast.LENGTH_SHORT).show()
                    }
                }

                override fun onImageSaved(output: ImageCapture.OutputFileResults) {
                    val savedUri = Uri.fromFile(photoFile)
                    val msg = "Photo capture succeeded: $savedUri"
                    imageView.post {
                        Glide.with(baseContext).load(photoFile).into(imageView)
                        captureLayout.visibility = View.VISIBLE
                        statusTv.text = imageUploading
                    }
                    filePath = photoFile.absolutePath
                    mainViewModel.uploadImage(filePath)
                    Log.e(TAG, "photoFile size =  ${photoFile.length()}")
                    Log.d(TAG, msg)
                }
            })
    }

    private fun getOutputDirectory(): File {
        val mediaDir = externalMediaDirs.firstOrNull()?.let {
            File(it, resources.getString(R.string.app_name)).apply { mkdirs() }
        }
        return if (mediaDir != null && mediaDir.exists())
            mediaDir else filesDir
    }

    /**
     * Process result from permission request dialog box, has the request
     * been granted? If yes, start Camera. Otherwise display a toast
     */
    override fun onRequestPermissionsResult(
        requestCode: Int, permissions: Array<String>, grantResults:
        IntArray
    ) {
        if (requestCode == REQUEST_CODE_PERMISSIONS) {
            if (allPermissionsGranted()) {
                startCamera()
            } else {
                Toast.makeText(
                    this,
                    "Permissions not granted by the user.",
                    Toast.LENGTH_SHORT
                ).show()
                finish()
            }
        }
    }

    /**
     * Check if all permission specified in the manifest have been granted
     */
    private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
        ContextCompat.checkSelfPermission(
            baseContext, it
        ) == PackageManager.PERMISSION_GRANTED
    }

    companion object {

        private const val TAG = "CameraXBasic"
        private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
        private const val REQUEST_CODE_PERMISSIONS = 10
        private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)

        /** Helper function used to create a timestamped file */
        private fun createFile(baseFolder: File, format: String, extension: String) =
            File(
                baseFolder, SimpleDateFormat(format, Locale.US)
                    .format(System.currentTimeMillis()) + extension
            )
    }

}
